package org.kongqian.car.service.impl;

import org.kongqian.basic.constants.BaseConstants;
import org.kongqian.car.domain.CarType;
import org.kongqian.car.mapper.CarTypeMapper;
import org.kongqian.car.service.ICarTypeService;
import org.kongqian.basic.service.impl.BaseServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author kq
 * @since 2022-11-03
 */
@Service
public class CarTypeServiceImpl extends BaseServiceImpl<CarType> implements ICarTypeService {
    @Autowired
    private CarTypeMapper carTypeMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    //这是手动写入缓存和注解自动写入缓存
//    @Override
//    @Cacheable(cacheNames=BaseConstants.CAR_TYPE_TREE_IN_REDIS,key="'ALL'")
//    public List<CarType> getCarTree(Long l) {
//
////        //手动写入缓存
////        //redis缓存使用流程
////        //先获取缓存中的值
////        Object redisData = redisTemplate.opsForValue().get(BaseConstants.CAR_TYPE_TREE_IN_REDIS);
////        //判断缓存中是否有值,有值就返回
////        if (Objects.nonNull(redisData)){
////            return (List<CarType>)redisData;
////        }
////        //没值就去数据库查，查完存一份在redis中
////        List<CarType> carTypeList = getCarTypes(l);
////        redisTemplate.opsForValue().set(BaseConstants.CAR_TYPE_TREE_IN_REDIS, carTypeList);
////        //最后返回数据
//
////        return carTypeList;
//        return getCarTypes(l);
//    }

    //互斥锁解决击穿的问题
    public List<CarType> getCarTree(Long l) {
        //Object redisData = redisTemplate.opsForValue().get(BaseConstants.CAR_TYPE_TREE_IN_REDIS);
        Object redisData =getRedisData(BaseConstants.CAR_TYPE_TREE_IN_REDIS,l);
        if (Objects.nonNull(redisData)){
            return (List<CarType>)redisData;
        }
        //互斥锁解决缓存击穿的问题
        synchronized (CarTypeServiceImpl.class) {
            //当第一个从数据库查询就把数据放在redis中，第二个直接从redis中获取就行了
//            Object redisData2 = redisTemplate.opsForValue().get(BaseConstants.CAR_TYPE_TREE_IN_REDIS);
            Object redisData2 =getRedisData(BaseConstants.CAR_TYPE_TREE_IN_REDIS,l);
            if (Objects.nonNull(redisData2)){
                return (List<CarType>)redisData2;
            }

            List<CarType> carTypeList = getCarTypes(l);
//            redisTemplate.opsForValue().set(BaseConstants.CAR_TYPE_TREE_IN_REDIS, carTypeList);

            setRedisData(BaseConstants.CAR_TYPE_TREE_IN_REDIS,carTypeList);
            return carTypeList;
        }
    }

    private void setRedisData(String key,Object object){
        //设置主key
       redisTemplate.opsForValue().set(key, object);
       //设置副key
        redisTemplate.opsForValue().set(key+"kq", object);
    }

    private Object getRedisData(String key,Long l){
        //设置主key
        Object redisData = redisTemplate.opsForValue().get(key);
        if (Objects.nonNull(redisData)){
            return redisData;
        }
        //设置副key
        redisData= redisTemplate.opsForValue().get(key + "kq");
        if (redisData!=null){
            List<CarType> carTypes = getCarTypes(l);
            setRedisData(key,carTypes);
            return carTypes;
        }
        //解决穿透问题
        return null;
    }
    //增删改要更新缓存中的数据，和数据库数据保持一致
    @Override
    @CacheEvict(cacheNames = BaseConstants.CAR_TYPE_TREE_IN_REDIS,key = "'ALL'")
    public void insert(CarType carType) {
        super.insert(carType);
//        //直接删除缓存，查询的时候重新存一份给redis
//        redisTemplate.delete(BaseConstants.CAR_TYPE_TREE_IN_REDIS);
    }

    @Override
    @CacheEvict(cacheNames = BaseConstants.CAR_TYPE_TREE_IN_REDIS,key = "'ALL'")
    public void deleteById(Serializable id) {
        super.deleteById(id);
//        //直接删除缓存，查询的时候重新存一份给redis
//        redisTemplate.delete(BaseConstants.CAR_TYPE_TREE_IN_REDIS);
    }

    @Override
    @CacheEvict(cacheNames = BaseConstants.CAR_TYPE_TREE_IN_REDIS,key = "'ALL'")
    public void updateById(CarType carType) {
        super.updateById(carType);
//        //直接删除缓存，查询的时候重新存一份给redis
//        redisTemplate.delete(BaseConstants.CAR_TYPE_TREE_IN_REDIS);
    }

    //双层for循环
    private List<CarType> getCarTypes(Long l){
        //创建一个容器来存储树类型并返回
        ArrayList<CarType> result = new ArrayList<>();
        //先查询出所有的类型
        List<CarType> carTypes = carTypeMapper.selectAll();

        //方案二：map
        HashMap<Long, CarType> objectObjectHashMap = new HashMap<>();
        for (CarType carType : carTypes) {
            objectObjectHashMap.put(carType.getId(), carType);
        }
        //循环遍历查询出所有的一级类型 pid==0
        for (CarType carType : carTypes) {
            if (carType.getPid().longValue()==l.longValue()){
                result.add(carType);
            }else {
                /**如果不是一级类型，再来循环一次，根据pid==id 来把子类型装到父类型的children对象中，那他就是子节点，
                 用子节点的pid和上级节点的id比较，相等的话就是上级类型的子类型**/
                //方案一：如果循环不是一级类型，我们唯一做的就是找到他的父类型，找完后结束循环
//                CarType parent=null;
//                for (CarType type : carTypes) {   //时间复杂度n*n
//                    if (type.getId().longValue()==carType.getPid().longValue()){
//                        parent=type;
//                        break;
//                    }
//                }

                /**第二种使用map来实现获取除一级类型下的父类型，创建一个map集合，key就是父类型的id,value就只对应的值，
                用get(子类型的pid)来获取父类型
                 **/
                CarType parent = objectObjectHashMap.get(carType.getPid()); //时间复杂度2*n
                //将子类放在父类型中
                parent.getChildren().add(carType);
            }
        }
        return result;
    }
    //递归的方式
//    private List<CarType> getCarTypes(long l) {
//        //这里要实现无限层级 采用递归
//        //1.先查一级类型
//        List<CarType> carTypes = carTypeMapper.selectTreeById(l);
//
//        //要改递归一个出口
//        if (carTypes==null){
//            return null;
//        }
//
//        //2.再遍历一级类型来查询一级类型下的子类型
//        for (CarType carType : carTypes) {
//            List<CarType> carTree = getCarTree(carType.getId());
//            carType.setChildren(carTree);
//        }
//        //一次类推下去就OK了
//        return carTypes;
//    }
}
